classdef dotsDrawableTextures < dotsDrawable
    % @class dotsDrawableTextures
    % Make and draw Psychtoolbox OpenGL textures.
    %
    % @ingroup drawable
    
    properties (SetObservable = true)
        % x-coordinate of the center of the drawn texture (degrees
        % visual angle, centered in window)
        x = 0;
        
        % y-coordinate of the center of the drawn texture (degrees
        % visual angle, centered in window)
        y = 0;
        
        % width of the drawn texture (degrees visual angle, centered in
        % window, defaults to the width of sourceRect)
        width = [];
        
        % height of the drawn texture (degrees visual angle, centered
        % in window, defaults to the height of sourceRect)
        height = [];
        
        % angle through which to rotate the drawn texture (degrees,
        % counterclockwise, 0 = rightward)
        rotation = 0;
        
        % fevalable cell array for creating arbitrary textures
        % @details
        % The function should expect the dotsDrawableTextures object as the
        % first argument.  Any other arguments in the cell array will be
        % passed to the function starting at the second place.
        % @details
        % The function should return an array of texture indexes as
        % obtained from the Screen('MakeTexture', ...) function.  It may
        % also set properties of the dotsDrawableTextures object such as
        % sourceRect.
        % @details
        % The function should expect to use properties of the
        % dotsDrawableTextures object such as windowNumber, windowRect,
        % windowFrameRate, pixelsPerDegree.
        textureMakerFevalable = {};
        
        % rectangular region from within each texture to draw to the window
        % (pixels, [x y x2 y2], defaults to the whole texture)
        sourceRect = [];
        
        % Psychtoolbox flag for how to stretch pixels
        % @details
        % See the Psychtoolbox Screen('DrawTextures?') for details about
        % filtering.
        filterMode = 0;
        
        % how opaque the drawn texture should appear (0=transparent through
        % 1=opaque, default 1)
        opacity = 1;
        
        % color values to multiply with each pixel in the texture ([RGB] or
        % [RGBA])
        falseColor = [];
    end
    
    properties (SetObservable = false)
        % array of Psychtoolbox texture indexes returned from
        % textureMakerFevalable.
        textureIndexes = [];
        
        % number of texture indexes to interleave
        nTextures = 0;
        
        % counter to keep track of interleaving textures
        frameNumber = 0;
        
        % rectangular region where to draw each texture on screen (pixels,
        % [x y x2 y2], defaults to size of texture, centered on x and y).
        destinationRect = [];
    end
    
    methods
        % Constructor takes no arguments.
        function self = dotsDrawableTextures
            self = self@dotsDrawable;
        end
        
        % Make a new texture(s) with textureMakerFevalable.
        function prepareToDrawInWindow(self)
            % get textures from black box texture maker function
            if isempty(self.textureMakerFevalable)
                self.nTextures = 0;
                
            else
                self.textureIndexes = feval( ...
                    self.textureMakerFevalable{1}, ...
                    self, ...
                    self.textureMakerFevalable{2:end});
                self.nTextures = numel(self.textureIndexes);
            end
            
            % prepare to draw the textures
            if self.nTextures > 0
                
                % which part of the textures to draw
                if isempty(self.sourceRect)
                    self.sourceRect = ...
                        Screen('Rect', self.textureIndexes(1));
                end
            end
        end
        
        % Draw textures that were made by textureMakerFevalable.
        function draw(self)
            nFrames = self.nTextures;
            if nFrames > 0
                frame = self.frameNumber;
                frame = 1 + mod(frame, nFrames);
                self.frameNumber = frame;
                
                % where to draw the textures in the window
                if isempty(self.width)
                    wPix = self.sourceRect(3)-self.sourceRect(1);
                else
                    wPix = round(self.pixelsPerDegree*self.width);
                end
                xPix = round(self.x*self.pixelsPerDegree ...
                    + self.windowRect(3)/2 - wPix/2);
                
                if isempty(self.height)
                    hPix = self.sourceRect(4)-self.sourceRect(2);
                else
                    hPix = round(self.pixelsPerDegree*self.height);
                end
                yPix = round(-self.y*self.pixelsPerDegree ...
                    + self.windowRect(4)/2 - hPix/2);
                
                self.destinationRect = [xPix, yPix, xPix+wPix, yPix+hPix];
                
                Screen('DrawTextures', ...
                    self.windowNumber, ...
                    self.textureIndexes(frame), ...
                    self.sourceRect, ...
                    self.destinationRect, ...
                    -self.rotation, ...
                    self.filterMode, ...
                    self.opacity, ...
                    self.falseColor);
            end
        end
    end
end